#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include "glext.h"
#include "wglext.h"
#include "Game.h"
#include "OpenGLExt.h"
WCHAR szClass[] = L"Game";
HWND hwnd = NULL;
HDC hdc = NULL;
HGLRC  hglrc;
HWND hwndDummy = NULL;
HDC hdcDummy = NULL;
HGLRC  hglrcDummy;
HINSTANCE hInstance = NULL;
MSG msg;
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam);
void Init();
void Release();
void InitOpenGLExt();
long long milliseconds_now() {
    static LARGE_INTEGER s_frequency;
    static BOOL s_use_qpc = QueryPerformanceFrequency(&s_frequency);
    if (s_use_qpc) {
        LARGE_INTEGER now;
        QueryPerformanceCounter(&now);
        return (1000LL * now.QuadPart) / s_frequency.QuadPart;
    } else {
        return GetTickCount();
    }
}
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)
{
	int iScreenWidth = 800;
	int iScreenHeight = 480;
	WNDCLASS wndclass;
	hInstance = hInst;
	wndclass.style = CS_HREDRAW|CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szClass;
	RegisterClass(&wndclass);
	hwnd = CreateWindow(szClass,TEXT("Window Title"),WS_OVERLAPPED|WS_CAPTION
		|WS_SYSMENU|WS_MINIMIZEBOX,
		CW_USEDEFAULT,CW_USEDEFAULT,iScreenWidth,iScreenHeight,NULL,NULL,hInstance,NULL);
	RECT rc;
	GetClientRect(hwnd,&rc);
	SetWindowPos(hwnd,NULL,0,0,2*iScreenWidth - rc.right,2*iScreenHeight - rc.bottom
		,SWP_NOMOVE|SWP_NOZORDER);
	hdc = GetDC(hwnd);
	Init();
	Game::CreateInstance();
	Game::GetInstance()->Init(iScreenWidth,iScreenHeight);
	ShowWindow(hwnd,true);
	ZeroMemory( &msg, sizeof(msg));
	long long start = milliseconds_now();
	while( msg.message!=WM_QUIT )
	{
		if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
		else
		{
			long long now = milliseconds_now();
			long long elapsed = now - start;
			start = now;
			Game::GetInstance()->Update(elapsed);
			Game::GetInstance()->Render();
			SwapBuffers(hdc);
			long long end = milliseconds_now();
			if(end - now < 16)
			{
				Sleep(16 - (end - now));
			}
		}
	}
	Game::GetInstance()->Release();
	Release();
	return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
	switch (message)
	{
		case WM_DESTROY:
			if(hWnd == hwnd)
			{
				PostQuitMessage(0);
			}
		return 0;
		case WM_MOUSEMOVE:
			
			return 0;
		case WM_LBUTTONDOWN:
			
			return 0;
		case WM_LBUTTONUP:
			
			return 0;
		case WM_RBUTTONDOWN:
			
			return 0;
		case WM_RBUTTONUP:
			
			return 0;
		case WM_KEYDOWN:
			Game::GetInstance()->OnKeyDown(wParam);
			return 0;
	}
	return DefWindowProc(hWnd,message,wParam,lParam);
}
void Init()
{
	hwndDummy = CreateWindow(szClass,TEXT("Window Title"),WS_OVERLAPPED|WS_CAPTION
		|WS_SYSMENU|WS_MINIMIZEBOX,
		CW_USEDEFAULT,CW_USEDEFAULT,100,100,NULL,NULL,hInstance,NULL);
	hdcDummy = GetDC(hwndDummy);

	PIXELFORMATDESCRIPTOR pfd = { 
    sizeof(PIXELFORMATDESCRIPTOR),   // size of this pfd 
    1,                     // version number 
    PFD_DRAW_TO_WINDOW |   // support window 
    PFD_SUPPORT_OPENGL |   // support OpenGL 
    PFD_DOUBLEBUFFER,      // double buffered 
    PFD_TYPE_RGBA,         // RGBA type 
    24,                    // 24-bit color depth 
    0, 0, 0, 0, 0, 0,      // color bits ignored 
    0,                     // no alpha buffer 
    0,                     // shift bit ignored 
    0,                     // no accumulation buffer 
    0, 0, 0, 0,            // accum bits ignored 
    32,                    // 32-bit z-buffer 
    0,                     // no stencil buffer 
    0,                     // no auxiliary buffer 
    PFD_MAIN_PLANE,        // main layer 
    0,                     // reserved 
    0, 0, 0                // layer masks ignored 
}; 

	GLint iPixelFormat;

	iPixelFormat = ChoosePixelFormat(hdcDummy, &pfd); 

	// make that the pixel format of the device context 
	int result = SetPixelFormat(hdcDummy, iPixelFormat, &pfd); 
	hglrcDummy = wglCreateContext (hdcDummy); 
 
	// make it the calling thread's current rendering context
	wglMakeCurrent (hdcDummy, hglrcDummy);
	InitOpenGLExt();
	PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
	wglMakeCurrent (NULL, NULL) ; 
	wglDeleteContext (hglrcDummy);
	DestroyWindow(hwndDummy);
	 int attributes[] = {
		   WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
		   WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
		   WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
		   WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
		   WGL_COLOR_BITS_ARB, 32,
		   WGL_DEPTH_BITS_ARB, 24,
		   //WGL_STENCIL_BITS_ARB, 8,
		   WGL_SAMPLE_BUFFERS_ARB, 1, //Number of buffers (must be 1 at time of writing)
		   WGL_SAMPLES_ARB, 0,        //Number of samples
		   0
		   };
	int pixelFormat;
	unsigned int numFormats;
	wglChoosePixelFormatARB(   hdc,
                                attributes,
                                NULL,
                                1,
                                &pixelFormat,
                                &numFormats);
	result = SetPixelFormat(hdc, pixelFormat, &pfd); 

	
	hglrc = wglCreateContext (hdc); 
 
	// make it the calling thread's current rendering context
	wglMakeCurrent (hdc, hglrc);
}
void Release()
{
	wglMakeCurrent (NULL, NULL) ; 
 
	// delete the rendering context 
	wglDeleteContext (hglrc); 
}

void InitOpenGLExt()
{
	const GLubyte* sExtensions = glGetString(GL_EXTENSIONS);

	glCreateShader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader");

	glShaderSource = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource");

	glCompileShader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader");

	glCreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram");

	glAttachShader = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader");

	glLinkProgram = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram");

	glUseProgram = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram");

	glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation");

	glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)wglGetProcAddress("glUniformMatrix4fv");

	glUniform1f = (PFNGLUNIFORM1FPROC)wglGetProcAddress("glUniform1f");

	glUniform2f = (PFNGLUNIFORM2FPROC)wglGetProcAddress("glUniform2f");

	glUniform3f = (PFNGLUNIFORM3FPROC)wglGetProcAddress("glUniform3f");

	glUniform4f = (PFNGLUNIFORM4FPROC)wglGetProcAddress("glUniform4f");

	glUniform1i = (PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i");

	glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)wglGetProcAddress("glGetAttribLocation");

	glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray");

	glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer");

	glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");

	glGenBuffers = (PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers");

	glBufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");

	glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)wglGetProcAddress("glBlendEquationSeparate");

	glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)wglGetProcAddress("glBlendFuncSeparate");

	glActiveTexture = (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture");

	glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)wglGetProcAddress("glDeleteBuffers");

	glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog");

	glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog");

	glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress("glGenFramebuffers");

	glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)wglGetProcAddress("glFramebufferTexture2D");

	glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)wglGetProcAddress("glCheckFramebufferStatus");

	glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)wglGetProcAddress("glDeleteFramebuffers");

	glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer");
}